'use strict'

const http = require('http');
const fs = require('fs')
const fsp = fs.promises

/**                         解析Body数据              */
/**
 * Content-Disposition:form-data;name:"file";filename:"xxx"
 * Content-Type:text/plain
 * @param {string} headerData 
 */

function parseBodyHeaders(headerData){
    let hlist = headerData.split('\r\n').filter(p=>p.length > 0 )

    // 解析content-disposition => hlist[0]
    // 按照 ; 切分数组，选出从第二个元素开始的数组
    let csplit = hlist[0].split(';').slice(1)

    let filename
    let name
    for (let a of csplit){
        if(a.indexOf('filename=')===0){
            filename = a.substring(a.indexOf('=')+2, a.length -1)
        }else{
            name=a.substring(a.indexOf('=')+2,a.length-1)
        }
    }

    let headers={}
    let ind = 0
    let k
    for(let h of hlist){
        ind = h.indexOf(':')
        k = h.substring(0, ind).toLowerCase()
        headers[k] = h.substring(ind+1).trim()
    }

    return{
        filename,
        name,
        headers,
        contentType:headers['content-type'] || ''
    }

}

function parseBody(bodyData,headers){
    let ctype = headers['content-type']

    let bdy = ctype.substring(ctype.indexOf('=') + 1 )

    let crlf_bdy = `\r\n--${bdy}`

    let header_end_index = bodyData.indexOf('\r\n\r\n',
                                    crlf_bdy.length)

    let headerData = bodyData.toString('utf8',
                                    crlf_bdy.length,
                                    header_end_index)

    // 解析文件头信息
    let hd= parseBodyHeaders(headerData)

    let file={
        start:header_end_index + 4,
        end:bodyData.indexOf(crlf_bdy,header_end_index),
        ...hd
    }

    file.length = file.end-file.start

    return file
}

// 解析文件扩展名
function parseExtName(filename){
    if(filename.length <2 ){
        return ''
    }

    let nsplit = filename.split('.').filter(p=>p.length > 0)

    if(nsplit.length < 2) return ''

    return `.${nsplit[ nsplit.length-1 ]}`
}



/**                       解析Body数据 - END         */



/*
.jpg .jpeg   =>  image/jepg
.png    =>   iamge/png
.gif    =>   image/gif
.ico    =>   image/x-ico
.txt    =>   text/plain
.json   =>   text/json 或 application/json
.xml    =>   text/xml  或 application/xml

*/


let html=`
    <!DOCTYPE html>
    <html>
        <head>
            <title>First HTTP</title>
        </head>
        <body>
            你好！
        </body>
    </html>
`

let routerTable = {

    GET : {
        '/':async(ctx)=>{
            response.setHeader('content-type',
                                'text/html;charset=utf-8')
            ctx.end(html)
        },

        '/test':async (ctx)=>{
            let status = parseInt(Math.random()*200) + 199
            ctx.response.statusCode = status
            ctx.end('test end')
        },

        '/upload':async (ctx)=>{
            let stm = fs.createReadStream('./upload.html')
            stm.pipe(ctx.response,{end:false})
            stm.on('end',()=>{
                ctx.end()
            })
        }
    },

    POST:{
        '/data':async (request,response)=>{

            let file=parseBody(ctx.rawBody,ctx.headers)
            console.log(file)

            if(!ctx.rawBody){
                ctx.end();
                return;
            }

            let fname = `${Date.now()}-${Math.random()}` + `${pathExtName(file.filename)}`
            let fh = await fsp.open(fname,'w+')
            let fret = await fh.write(ctx.rawBody,file.start,file.length,0)

            // 关闭打开的文件
            fh.close();
            
            ctx.end(`${fret.bytesWritten}`)

            // let bufferList = [];
            // let bufferLength = 0;

            // request.on('data',chunk=>{
            //     bufferLength += chunk.length
            //     bufferList.push(chunk)
            // })

            // let bodyData = null;
            // request.on('end',()=>{
            //     if (bufferList.length > 0){
            //         bodyData = Buffer.concat(bufferList,bufferLength)
            //         bufferList = []
            //     }

            //     let file = parseBody(bodyData, request.headers)
            //     console.log(file)

            //     if (bodyData){
            //         response.end(bodyData)
            //     }else{
            //         response.end()
            //     }
            // })
        }
    }
}

http.createServer(async(request,response)=>{

    let rm = routerTable[request.method]

    let usplit=request.url.split('?')

    let pathname=usplit[0]
    let querystring=usplit[1] || ''
    
    if(!rm || ! rm [ request.url ]){
        response.statusCode = 404
        response.end('page not found')
        return
    }

    // ctx 是 context 的缩写，意为请求上下文
    // 就是一个打包了各种数据和方法的对象
    let ctx={
        method:request.method,
        path:pathname,
        search:querystring,
        headers:request.headers,
        request:request,
        response:response,
        end:(data,encoding='utf8')=>{
            response.end(data,encoding)
        },
        write:(data,encoding='utf8')=>{
            response.write(data,encoding)
        },
        totalLength:0,
        rawBody:null,
        bufferList:[]
    }

    //  get  head  trace  options
    //  post  put  patch  delete  

    await new Promise((rv,rj)=>{
        if('PD'.indexOf(request.method[0]) >= 0){
            request.on('data',chunk=>{
                ctx.bufferList.push(chunk)
                ctx.totalLength += chunk.length
            })
        }else{
            request.on('data',data=>{
                response.statusCode=400
                response.end('bad request',()=>{
                    request.destroy()
                })
            })
        }

        let resolved = false;
        request.on('end',()=>{
            resolved=true;
            rv();
        })

        request.on('closed',()=>{
            (!resolved) && rv();
        })
    })

    if(!response.writable || response.writableEnded){
        return 
    }

    if(ctx.bufferList.length >0 ){
        ctx.rawBody=Buffer.concat(ctx.bufferList,ctx.totalLength)
        ctx.bufferList=[]
    }

    rm[ pathname ](ctx)
    // rm[ request.url ](request,response)


}).listen(3456);